<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>类型化数组（Typed Arrays）</title>
</head>

<body>
    <script>
        // JavaScript 类型数组（Typed Arrays）将实现拆分为缓冲和视图两部分。
        // 一个缓冲（由 ArrayBuffer 对象实现）描述的是一个数据块。
        // 缓冲没有格式可言，并且不提供机制访问其内容。
        // 为了访问在缓冲对象中包含的内存，你需要使用视图。
        // 视图提供了上下文 — 即数据类型、起始偏移量和元素数 — 将数据转换为实际有类型的数组。
        window.onload = function () {
            (function () {
                // 数组缓冲类型  只是内存中指定的字节数，
                // 并不表示实际存储是什么类型的数据，通过ArrayBuffer只是在内存中分配一定数量的字节
                var buffer = new ArrayBuffer(20);
 
                // DataView 视图是一个可以从 ArrayBuffer 对象中读写多种数值类型的底层接口
                // 可以从ArrayBuffer取得一小段字节读写数据
                // 传入buffer，可选的字节偏移量 默认为0(从哪个字节开始)，可选的要利用的字节数 默认全部
                var view = new DataView(buffer, 0);

                // 在第一个字节除存入16位的整数42 
                view.setInt16(0, 42);
                view.setInt16(2, 50);
                //不能从字节1开始，因为16位整数需要2b来存储 所以存储42时用了0和1两个字节

                console.log(view.getInt16(0)) //42
            })();

            (function () {
                // 类型化视图。也称类型化数组，因为它们除了元素必须是某种类型的数据，与常规的数组并无区别
                // 它们都继承DataView因此传入的参数与DataView实例化都是一样的
                // 传入buffer，可选的字节偏移量 默认为0(从哪个字节开始)，可选的要利用的字节数 默认全部

                var buffer = new ArrayBuffer(20);
                // 创建一个新数组 使用整个缓冲器
                var int8s = new Int8Array(buffer);

                // 只使用从9字节处开始的缓冲器
                //var int16s = new Int16Array(buffer,9);

                // 使用从9字节到18字节的缓冲器
                //var int16s = new Int16Array(buffer, 9, 10);

                // 每个构造视图函数都有一个BYTES_PER_ELEMENT属性 表示类型化数组的每个元素需要多少个字节
                // 因此 Uint8Array.BYTES_PER_ELEMENT 就是1 而Float32Array.BYTES_PER_ELEMENT 就是4 可以利用这个辅助初始化

                // 存入5个8位数元素需要 5个字节空间
                //var int8s = new Int8Array(buffer, 0, 5 * Int8Array.BYTES_PER_ELEMENT);

                // 从上一个8位数之后 再存入5个16位元素 需要10个字节空间 16位需要2B的字节是8位数的两倍
                //var uint16s = new Int8Array(buffer, int8s.byteOffset + int8s.byteLength, 5 * Int32Array.BYTES_PER_ELEMENT);
            })();
            (function () {
                // 类型化视图的目的在于简化对二进制的操作
                // 因此创建类型化视图可以不用传入ArrayBuffer对象，只要传入希望数组保存的 元素数
                // 相应的构造函数就可以自动创建一个包含足够字节数的ArrayBuffer对象

                //创建一个数组保存10个 8位整数 (自动创建10字节的缓冲区)
                var int8=new Int8Array(10);

                //创建一个数组保存5个 16位整数 (自动创建10字节的缓冲区)
                var int16=new Int16Array(5);
                //创建一个数组保存5个 32位整数 (自动创建20字节的缓冲区)
                var int8=new Int32Array(5);

                // 也可以把常规数组转换为类型化数组，只需要把常规数组传入类型化视图的构造函数中即可
                // 创建一个数组保存5个 8位整数（5字节）
                var arr=[10,20,30,40,50];
                var int8s=new Int8Array(arr);
                // 这是默认来初始化类型化视图的最佳方式，也是WebGL项目中最常用的方法
                console.log(int8s.byteLength); //5个字节长度

                // 对类型化视图的迭代与对Array对象的迭代就是一样的
                for (let i = 0; i < int8s.length; i++) {
                    const element = int8s[i];
                    console.log("Value at position: "+i+" is "+element)
                }
                
                // 使用subarray方法可以基于底层数组缓冲器的子集创建一个新视图
                // 传入开始元素的索引 和 可选的结束元素的索引 返回的类型与调用该方法的视图类型相同
                // 意思就是uint16s和sub是同一个ArrayBuffer对象 只不过sub是unit16s的一个子集 类型是一样的
                // 只不过操作uint16s里的数值时不必担心sub里保存的数值会被修改
                var uint16s=new Uint16Array(10);
                var sub=uint16s.subarray(2,5);
            })()
        }
    </script>
</body>

</html>